package com.muyu.service.impl;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.word.WordExportUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.muyu.api.enums.ApiStatusEnum;
import com.muyu.api.exception.BusinessException;
import com.muyu.mapper.UserMapper;
import com.muyu.model.entity.LogInfoEntity;
import com.muyu.model.entity.UserEntity;
import com.muyu.model.excel.ExportExcelLogInfo;
import com.muyu.model.excel.ExportExcelUser;
import com.muyu.model.excel.ExportUser;
import com.muyu.model.excel.ImportUser;
import com.muyu.service.LogInfoService;
import com.muyu.service.UserService;
import com.muyu.util.EasypoiUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @ClassName: UserServiceImpl
 * @Author: Zirun.Xin
 * @Date: 2025/4/9 下午3:59
 * @Version: 1.0
 * @Description: 用户业务实现层
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    /**
     * 用户持久层
     */
    private final UserMapper userMapper;

    /**
     * 日志信息业务层
     */
    private final LogInfoService logInfoService;

    /**
     * excel批量导入用户
     *
     * @param file excel文件
     * @return 是否成功
     */
    @Override
    public Boolean importUsers(MultipartFile file) {
        ImportParams importParams = new ImportParams();

        // 标题行设置为1行，默认是0，可以不设置，依据实际情况设置
        importParams.setTitleRows(1);
        // 表头设置为1行
        importParams.setHeadRows(1);

        List<ImportUser> importUserList;
        try {
            // 读取excel
            importUserList = ExcelImportUtil.importExcel(file.getInputStream(), ImportUser.class, importParams);
        } catch (Exception e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        }

        return this.saveUsers(importUserList);
    }

    /**
     * excel批量导出用户
     *
     * @param response servlet响应
     */
    @Override
    public void exportUsers(HttpServletResponse response) {
        // 从数据库查到数据
        List<UserEntity> userEntityList = this.list();

        // 结构转换映射
        List<ExportUser> exportUserList = this.mapExportUser(userEntityList);

        // 设置下载名称
        try {
            // 设置excel参数
            ExportParams params = new ExportParams();
            // 设置sheet名
            params.setSheetName("用户列表");
            // 设置标题
            params.setTitle("用户信息表");

            // 设置信息头，告诉浏览器内容为excel类型
            response.setHeader("Content-Type",
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            // 设置下载名称
            response.setHeader("Content-Disposition", String.format("attachment;filename=%s",
                    URLEncoder.encode("用户信息表.xlsx", StandardCharsets.UTF_8.name())));

            // 字节流输出
            ServletOutputStream out = response.getOutputStream();

            // 导出excel
            Workbook workbook = ExcelExportUtil.exportExcel(params, ExportUser.class, exportUserList);
            workbook.write(out);
        } catch (Exception e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        }
    }

    /**
     * excel批量导出用户（携带图片版）
     *
     * @param response servlet响应
     */
    @Override
    public void exportUsersToExcel(HttpServletResponse response) {
        // 从数据库查询到数据
        List<UserEntity> userEntityList = this.list();

        // 结构转换映射
        List<ExportExcelUser> exportExcelUserList = this.mapExportExcelUser(userEntityList);

        try {
            EasypoiUtils.exportExcel(response, "用户列表", "用户信息表",
                    "用户信息表", ExportExcelUser.class, exportExcelUserList);
        } catch (IOException e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        }
    }

    /**
     * word文档导出到一个模板中
     *
     * @param response servlet响应
     */
    @Override
    public void exportUsersToWordAll(HttpServletResponse response) {
    }

    /**
     * word文档导出到一个模板中
     *
     * @param response servlet响应
     */
    @Override
    public void exportUsersToWord(HttpServletResponse response) {
        // 查询所有用户数据
        List<UserEntity> userEntityList = this.list();

        List<Map<String, Object>> dataMapList = userEntityList.stream().map(userEntity -> {
            Map<String, Object> map = new HashMap<>();
            map.put("name", userEntity.getName());
            map.put("age", userEntity.getAge());
            map.put("sex", userEntity.getSex());
            map.put("address", userEntity.getAddress());
            map.put("describes", userEntity.getDescribes());
            map.put("image", this.imgFormatting(userEntity.getImage(), 100, 100));

            return map;
        }).collect(Collectors.toList());

        try {
            // 设置信息头，告诉浏览器内容为excel类型
            response.setHeader("Content-Type",
                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            // 设置下载名称
            response.setHeader("Content-Disposition", String.format("attachment;filename=%s",
                    URLEncoder.encode("用户基本信息.docx", StandardCharsets.UTF_8.name())));

            // 导出word并指定word导出模板
            XWPFDocument doc = WordExportUtil.exportWord07("./template/用户导出模板.docx", dataMapList);
            // 写入word文件
            doc.write(response.getOutputStream());
        } catch (Exception e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        }
    }

    /**
     * excel多sheet导入
     *
     * @param file excel文件
     */
    @Override
    public void importForSheetUsers(MultipartFile file) {
        // 分批进行读取excel的sheet工作表
        try {
            // 读取第一个sheet表
            List<ImportUser> sheetOneUsers = EasypoiUtils.importExcel(
                    file.getInputStream(), 0, 1, 1, ImportUser.class);
            // 读取第二个sheet表
            List<ImportUser> sheetTwoUsers = EasypoiUtils.importExcel(
                    file.getInputStream(), 1, 1, 1, ImportUser.class);

            //批量插入数据
            this.saveUsers(sheetOneUsers);
            this.saveUsers(sheetTwoUsers);
        } catch (IOException | EasypoiUtils.ExcelImportException e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        }
    }

    /**
     * excel多sheet导出
     *
     * @param response servlet响应
     */
    @Override
    public void exportForSheetUsers(HttpServletResponse response) {
        //获取用户数据
        List<UserEntity> userEntityList = this.list();
        // 转换用户结构体
        List<ExportExcelUser> exportExcelUserList = this.mapExportExcelUser(userEntityList);

        // 创建参数对象（用来设定excel的sheet1内容等信息）
        ExportParams userExportParams = new ExportParams();
        // 设置sheet得名称
        userExportParams.setSheetName("用户表");
        // 设置sheet表头名称
        userExportParams.setTitle("用户列表");

        // 创建sheet1使用的map
        Map<String, Object> userExportMap = new HashMap<>();
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        userExportMap.put("title", userExportParams);
        // 模版导出对应得实体类型
        userExportMap.put("entity", ExportExcelUser.class);
        // sheet1中要填充得数据
        userExportMap.put("data", exportExcelUserList);

        // 获取日志信息数据
        List<LogInfoEntity> logInfoEntityList = logInfoService.list();
        // 转换日志信息结构体
        List<ExportExcelLogInfo> exportExcelLogInfoList = logInfoService.mapExportExcelLogInfo(logInfoEntityList);


        // 创建参数对象（用来设定excel的sheet2内容等信息）
        ExportParams logInfoExportParams = new ExportParams();
        logInfoExportParams.setTitle("日志列表");
        logInfoExportParams.setSheetName("日志表");

        // 创建sheet2使用的map
        Map<String, Object> logInfoExportMap = new HashMap<>();
        logInfoExportMap.put("title", logInfoExportParams);
        logInfoExportMap.put("entity", ExportExcelLogInfo.class);
        logInfoExportMap.put("data", exportExcelLogInfoList);

        // 将sheet1、sheet2使用得map进行包装
        List<Map<String, Object>> sheetsList = new ArrayList<>();
        //后续增加sheet组，则后面继续追加即可;
        sheetsList.add(userExportMap);
        sheetsList.add(logInfoExportMap);

        //功能描述：把同一个表格多个sheet测试结果重新输出，
        Workbook workbook = null;

        try {
            //设置编码格式
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            //设置内容类型
            response.setContentType("application/octet-stream");
            //设置头及文件命名
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode("用户及操作日志导出.xls", StandardCharsets.UTF_8.name()));

            // 执行方法
            workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
            //写出流
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            log.error("excel多sheet导出异常，异常信息：【{}】", e.getMessage());
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        } finally {
            if (workbook != null) {
                try {
                    // 强行关流
                    workbook.close();
                } catch (IOException e) {
                    log.error("workbook关流异常，异常信息：【{}】", e.getMessage());
                    throw new BusinessException(
                            ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
                }
            }
        }
    }

    /**
     * 导出pdf文件
     *
     * @param response servlet响应
     */
    @Override
    public void exportPdfUsers(HttpServletResponse response) {
        // 定义一个原子序列
        AtomicInteger atomicInteger = new AtomicInteger(1);
        // 从数据库中获取数据
        List<UserEntity> userEntityList = this.list();
        // 数据转换
        List<HashMap<String, Object>> exportDataMapList = userEntityList.stream()
                .map(userEntity -> new HashMap<String, Object>() {{
                    put("id", atomicInteger.getAndIncrement());
                    put("name", userEntity.getName());
                    put("age", userEntity.getAge());
                    put("sex", userEntity.getSex());
                    put("address", userEntity.getAddress());
                    put("describes", userEntity.getDescribes());
                }})
                .collect(Collectors.toList());
        //定义导出数据容器
        Map<String, Object> exportDataMapListMap = new HashMap<>();
        exportDataMapListMap.put("userData", exportDataMapList);

        // 指定excel模板，在资源目录下的文件
        TemplateExportParams params = new TemplateExportParams("./template/导出excel模板.xlsx");

        // 定义一个字节输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // 将excel文件写入到新的输出流
        try {
            //调用excel导出方法
            Workbook workbook = ExcelExportUtil.exportExcel(params, exportDataMapListMap);
            //将excel文件写入到新的输出流
            workbook.write(outputStream);

            //将字节数组放置到内存里面
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());

            //导入xls包引用Workbook区分poi提供的Workbook
            com.spire.xls.Workbook spireWorkbook = new com.spire.xls.Workbook();
            spireWorkbook.loadFromStream(inputStream);
            // 设置字段在一页中显示全
            spireWorkbook.getConverterSetting().setSheetFitToPage(true);

            // 创建PDF输出流
            ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
            spireWorkbook.saveToStream(pdfOutputStream, com.spire.xls.FileFormat.PDF);

            // 设置响应头
            response.reset();
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment;filename=" +
                    URLEncoder.encode("用户基本信息表.pdf", StandardCharsets.UTF_8.name()));
            response.setContentLength(pdfOutputStream.size());

            // 将PDF写入响应输出流
            ServletOutputStream servletOutputStream = response.getOutputStream();
            pdfOutputStream.writeTo(servletOutputStream);
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(), ApiStatusEnum.INTERNAL_ERROR.getDesc());
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                throw new BusinessException(ApiStatusEnum.INTERNAL_ERROR.getCode(),
                        ApiStatusEnum.INTERNAL_ERROR.getDesc());
            }
        }
    }

    /**
     * 批量保存用户
     *
     * @param importUserList 保存数据集
     * @return 保存是否成功
     */
    private boolean saveUsers(List<ImportUser> importUserList) {
        if (CollectionUtils.isEmpty(importUserList)) {
            return false;
        }

        List<UserEntity> userEntityList = importUserList.stream().map(importUser ->
                UserEntity.builder()
                        .name(importUser.getName())
                        .age(importUser.getAge())
                        .sex(importUser.getSex())
                        .address(importUser.getAddress())
                        .describes(importUser.getDescribes())
                        .image(String.format("./template/image/%s.jpg", importUser.getName()))
                        .companyId(1)
                        .build()
        ).collect(Collectors.toList());

        return this.saveBatch(userEntityList);
    }

    /**
     * 映射ExportUser对象
     *
     * @param list 用户实体数据
     * @return 导出用户对象集合
     */
    private List<ExportUser> mapExportUser(List<UserEntity> list) {
        return list.stream().map(userEntity ->
                ExportUser.builder()
                        .name(userEntity.getName())
                        .age(userEntity.getAge())
                        .sex(userEntity.getSex())
                        .address(userEntity.getAddress())
                        .describes(userEntity.getDescribes())
                        .build()
        ).collect(Collectors.toList());
    }

    /**
     * 映射ExportExcelUser对象
     *
     * @param list 用户实体数据
     * @return 导出用户对象集合
     */
    private List<ExportExcelUser> mapExportExcelUser(List<UserEntity> list) {
        return list.stream().map(userEntity ->
                ExportExcelUser.builder()
                        .name(userEntity.getName())
                        .age(userEntity.getAge())
                        .bornDate(new Date())
                        .enterSchoolTime("20260101")
                        .sex(userEntity.getSex())
                        .address(userEntity.getAddress())
                        .image(userEntity.getImage())
                        .describes(userEntity.getDescribes())
                        .build()
        ).collect(Collectors.toList());
    }

    /**
     * 图片格式化，Word导出图片格式
     *
     * @param imgPath 图片路径
     * @param width   图片宽度
     * @param height  图片高度
     * @return 图片实体
     */
    private ImageEntity imgFormatting(String imgPath, int width, int height) {
        // 设置图片
        ImageEntity imageEntity = new ImageEntity(imgPath, width, height);
        // 表格外添加简单图片
        imageEntity.setType(ImageEntity.URL);

        return imageEntity;
    }
}
